package consultas;

import interfazGrafica.Grafo.Arista;
import interfazGrafica.Grafo.Punto;

import java.awt.Color;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.lang.Exception;

import estructuraGrafo.Arco;
import estructuraGrafo.Grafo;
import estructuraGrafo.Nodo;
import auxiliar.Monticulo;
import auxiliar.NodoMont;

/**
 * Clase ConsultaA: Se encarga de obtener el camino mas corto de grafo determinado
 * 
 * @author  Grupo N11
 * @author  Prinsich Bernz, Emilio J. A.
 * @author 	Solia, Guillermo D.
 * @version  1.0
 */

public class ConsultaB implements Serializable {

	private static final long serialVersionUID = 1L;
	/**Lista con los peajes del camino.*/
	private ArrayList<String> recorrido;
	/**Lista con las aveniadas del camino.*/
	private ArrayList<String> listaAvenidas;
	/**Total de costo en pesos del camino.*/
	private double totalCosto;
	
	public ConsultaB(){
		recorrido = new ArrayList<String>();
		listaAvenidas = new ArrayList<String>();
		totalCosto = 0.0;
	}
	
	/** Obtiene el camino mas economico desde un peaje inicial hasta otro peaje final
	 * 
	 * @param graph
	 * 				Grafo a recorrer
	 * @param nombreInicio
	 * 				Nombre del peaje donde comienza el camino
	 * @param nombreFin
	 * 				Nombre del peaje donde comienza el camino
	 * @throws Exception
	 * 				Se produce cuando no se puede alcanzar el peaje final
	 */
	
	public void consultar(Grafo graph, String nombreInicio, String nombreFin)throws Exception{
		//contiene los menores costos para lleger desde el nodo origen
		double distancia [] = new double[graph.size()]; 
		//contiene el indice a la lista de nodos al nodo anterior de costo menor
		int anterior[] = new int[graph.size()]; 
		/*contiene el nombre de la avenida a tomar, para llegar al nodo que se encuentra en la variable "anterior",
		utilizando el mismo indice*/
		String avenidas [] = new String [graph.size()];
		//Lista de todos los arcos del grafo
		ArrayList<Arco> arcos = graph.getAristas();
		//Nodo temporales. Se explicara su uso mas adelante
		Nodo temp,temp2;
		
		int indice;
		//Lista de todos los nodos(peajes) del grafo
		List<Nodo> listaNodos = graph.getListaNodos();
		/*Lista de nodos del monticulo. Se utiliza para poder actualizar los valores de sus claves por 
		fuera del monticulo */
		List<NodoMont> listaNodosMont = new ArrayList<NodoMont>();
		//nodo origen del recorrido
		Nodo inicio = graph.getNodo(nombreInicio); 
		//indice al nodo final del recorrido
		int fin = (graph.getNodo(nombreFin)).getNumero();
		
		/* Monticulo que contendra los nodos del grafo, de acuerdo al costo de llegar desde el peajes inicial.
		 * Se le insertan NodoMont, que contienen el nodo y el costo de llegar desde el peaje inical
		 */
		Monticulo vertices = new Monticulo();
		
		// se inicializan todos los costos a infinito
		for(int i = 0; i < distancia.length; i++)
			distancia[i] = Double.POSITIVE_INFINITY; 
				
		
		//costo al nodo origen
		distancia[inicio.getNumero()] = 0.0;
		//se insertar los nodos en el moticulo
		for(int i = 0; i < listaNodos.size(); i++){
			temp = listaNodos.get(i);			
			indice = (listaNodos.get(i)).getNumero();
			listaNodosMont.add(new NodoMont(temp,distancia[indice]));
			vertices.insertar(listaNodosMont.get(i));
		}	
		
		//Mientras el monticulo no este vacio
		while(!(vertices.esVacio())){
			temp = vertices.eliminarMinimo();//contiene el nodo que tiene menor costo para llegar desde el nodo inicial
			
			//se recorre la lista de arcos a fin determinar los costos menores
			for(int j = 0; j < arcos.size(); j++){
				//Si la arista sale del nodo temp
				if((((arcos.get(j)).getInicial()).equals(temp.getNombre()))){
					//Nodo destino de la arista
					temp2 = graph.getNodo((arcos.get(j)).getTerminal());
					//Indice del nodo destino al arreglo distancia
					indice = temp2.getNumero();
					/*Si el costo actual al nodo destino es mayor al costo que surge de sumar los costos
					al nodo sacado del moticulo y el precio de la avenida(arista) actual, que tiene que estar habilitada*/
					if((distancia[indice] > distancia[temp.getNumero()] + (arcos.get(j)).getCosto()) && ((arcos.get(j)).isEstado())){
						distancia[indice] = distancia[temp.getNumero()] + (arcos.get(j)).getCosto();
						anterior[indice] = temp.getNumero();
						avenidas[indice] = (arcos.get(j)).getNombre();
						//actualiza el valor del costo del nodo destino al nodo inicial, en el moticulo
						(listaNodosMont.get(indice)).setClave(distancia[indice]);
						//se reacomoda el monticulo
						vertices.flotar((listaNodosMont.get(indice)).getIndice());
					}//FIN Si
				}//FIN Si
			}
				
			
		}//FIN Mientras
		
		//Si el peaje final es inalcanzable
		if(distancia[fin] == Double.POSITIVE_INFINITY)
			throw new Exception("El peaje es inalcanzable");
		
		//Se actualiza el total de Km del camino mas economico
		totalCosto = distancia[fin];
		
		Stack<Integer> pilaPeajes = new Stack<Integer>();
		Stack<String> pilaAvenidas = new Stack<String>();
		ArrayList<String> resultadoPeajes = new ArrayList<String>();
		ArrayList<String> resultadoAvenidas = new ArrayList<String>();
		
		int k = fin;
		pilaPeajes.push(fin);
		
		while(anterior[k] != inicio.getNumero()){
			pilaPeajes.push(anterior[k]);
			pilaAvenidas.push(avenidas[k]);
			k = anterior[k];
		}
		pilaPeajes.push(anterior[k]);
		pilaAvenidas.push(avenidas[k]);
		
		
		while(!(pilaPeajes.isEmpty()))
			resultadoPeajes.add((listaNodos.get(pilaPeajes.pop())).getNombre());
					
		while(!(pilaAvenidas.isEmpty()))
			resultadoAvenidas.add(pilaAvenidas.pop());
		
		recorrido = resultadoPeajes;
		listaAvenidas = resultadoAvenidas;
		
		
	}
	
	/**Genera un String con el camino a seguir del ultimo calculo que se realizo
	 * 
	 * @return
	 * 		String con que describe camino menos costoso a segir
	 */
	
	public String getText(){
		String resultado = new String("");
		int i;
		for(i = 0; i < recorrido.size() - 1; i++)
			resultado = resultado + recorrido.get(i) + ": " + listaAvenidas.get(i) + "\n";
		resultado = resultado + recorrido.get(i) + "\nTotal costo: $" + totalCosto;
		return resultado;
		
	}
	
	/**
	 * Pinta el grafo que esta en el Linezo
	 * @param aristas Arista que representan los enlaces
	 * @param puntos Puntos que representan los nodos
	 */
	public void pintar(ArrayList<Arista> aristas, ArrayList<Punto> puntos){
		boolean t = true;

		//Pinta las Aristas
		for(Arista aris : aristas){
			for(String link : listaAvenidas){
				if(link.equals(aris.getNombre())){
					aris.setColor(Color.RED);
				}
			}
		}

		//Pinta los Nodos
		for(Punto point : puntos){
			for(String node : recorrido){
				if(point.getNombre().equals(node)){
					if(t){
						point.setColorPunto(Color.RED);
						t = false;
					}
					else
						point.setColorPunto(Color.GREEN);
				}
			}
		}
	}

}